import Evt from './event'
import Algo from './algo'
import {EN,Action,Web} from './config'
import {
	ActionGroup,
	PathEditWhiteList,
	PathQueryWhiteList,
	EndPointPathBlackList,
	PathRemoveWhiteList} from './config/action-auth'

const __v = Symbol("version_list")

export default class Version extends Evt{
	constructor($env){
		super()
		this[__v] = []
		this.pointsSize = 0
		this.$env = $env
		this.scaleSpecialList = [
			Action.FunctionTool
		]
	}
	_updatePS(n){
		this.pointsSize+=n
	}
	ID(){
		return Web.prefix+ Math.random().toFixed(6).toString().replace(/\./,"")
	}
	queryById(id){
		let isArr = (id instanceof Array)
		let ids = isArr?id:[id]
		let rs = this.lastVersion(r=>ids.includes(r.id))
		return isArr?rs:rs[0]
	}
	queryByAction(action,filter,r){
		let tmpAs = (action instanceof Array)?action:[action]
		let rs=
		(r||this.lastVersion(filter))
		.filter((step)=>tmpAs.includes(step.action))
		return rs
	}
	queryByActionAndPos(action,pos){
		return this.queryByAction(action).filter(p=>{
			return Algo[p.action](p.step,pos,p.state)
		})
	}
	queryInPath(pos){
		let action = this.$env.get("currentAction")
		let whiteList = []
		switch(action){
			case Action.PathRemove:
				whiteList = PathRemoveWhiteList
				break;
			case Action.PathFocus:
				whiteList = PathEditWhiteList
				break
			case Action.CircleCut:
				whiteList = ActionGroup.circles
				break
			case Action.CrossPoint:
			case Action.MirrorLine:
				whiteList = ActionGroup.lines
				break
			default:
				whiteList = PathQueryWhiteList
		}
		return this.queryPath(pos,whiteList)
	}
	queryPath(pos,whiteList=[]){
		let rs= this.queryByAction(whiteList).filter(r=>{
			return Algo[r.action](r.step,pos,r.state,this.$env)
		})
		return rs
	}
	queryEditablePath(pos){
		return this.queryPath(pos,PathEditWhiteList)
	}
	queryRemovablePath(pos){
		return this.queryPath(pos,PathRemoveWhiteList)
	}
	queryInPathPoint(pos){
		let r = this.queryInPath(pos)
		// let rmap = {}
		let idx = r.map(s=>{
			return Algo.findIndex(s.step,pos)
		})
		let l = this.lastVersion().filter(s=>{
			return !r.some(is=>is.id===s.id)
		})
		return {r,l,idx}
	}
	status(){}
	create(cache){
		let nv = [...cache.l,...cache.r]
		this[__v].push(nv)
		this.emit(EN.commit,[...cache.r])
	}
	commit(action,data,id){
		if(id){
			this.update(data,id)
			return
		}
		console.log("commit",action,data)
		id = this.ID()+Date.now()
		let nv = this.lastVersionNoFilter()
		let stepData = {action,id,...data}
		nv.push(stepData)
		if(!ActionGroup.noPoint.includes(action))
			this._updatePS(data.step.length)
		this[__v].push(nv)
		this.emit(EN.commit,[stepData])
	}
	update(data,id){
		console.log("update",data.action,data)
		let nv = this.lastVersionNoFilter()
		let step = nv.filter((s)=>s.id==id)[0]
		if(step){
			step.merge(data)
			this[__v].push(nv)
			this.emit(EN.update,step.assign())
		}
	}
	updateMulti(data){
		let nv = this.lastVersionNoFilter()
		data.forEach(d=>{
			let step = nv.filter((s)=>s.id==d.id)[0]
			if(step)
				step.merge(d)
			console.log(step)
		})
		this[__v].push(nv)
		this.emit(EN.create)
	}
	updateByCommand(data,id){
		let nv = this.lastVersionNoFilter()
		let step = nv.filter((s)=>s.id==id)[0]
		if(step){
			step.merge(this.mergeByCommand(data,step))
			this[__v].push(nv)
			this.emit(EN.update)
		}
	}
	mergeByCommand(data,step){
		step=step.assign()
		let {value,command} = data
		let path = command.split(".")
		if(path[0]==="step")
			step[path[0]][path[1]][path[2]]=value
		else if(path[0]==="state")
			step[path[0]][path[1]]=value
		return step
	}
	remove(id){
		let nv = this.lastVersionNoFilter()
		let tmpIds = (id instanceof Array)?id:[id]
		tmpIds.forEach(id=>{
			let idx = this.findIndex(nv,id)
			nv.splice(idx,1)
		})
		this[__v].push(nv)
		this.emit(EN.remove,id)
	}
	queryEndPoint(pos){
		let r = this.lastVersion().filter(s=>!EndPointPathBlackList.includes(s.action))
		return this.flat(r).filter((p)=>{
			return Math.distance(pos,p) < Web.r
		}).sort((fp,sp)=>{
			return Math.distance(pos,fp)-Math.distance(pos,sp)
		})
	}
	// translate(dx,dy){}
	scale(pr,r){
		this[__v].forEach(nv=>{
			this.doScale(nv,pr,r)
		})
		this.emit(EN.create)
	}
	doScale(nv,pr,r){
		nv.forEach((s)=>{
			if(!this.scaleSpecialList.includes(s.action))
			s.step.forEach((d)=>{
				d.x/=pr
				d.x*=r
				d.y/=pr
				d.y*=r
				if(d.w){
					d.w/=pr
					d.w*=r
				}	
				if(d.h){
					d.h/=pr
					d.h*=r
				}	
			})
			s.state.zoom = r
		})
		return nv
	}
	pop(){
		this[__v].pop()
		this.emit(EN.pop)
	}
	clear(){
		this[__v]=[]
		this.emit(EN.clear)
	}

	lastVersion(filter){
		let rs = this[__v].last()||[]
		if(!filter)
			filter = s=>s.state.visible
		return rs.filter(filter).clone()
	}
	lastVersionNoFilter(){
		return this.lastVersion(s=>true)
	}
	flat(r){
		return r.map((s)=>s.step).flat()
	}
	findIndex(list,id){
		return list.findIndex((k)=>k.id===id)
	}
}